package com.coocaa.ops.plus.dashboard.v1.codegen.provider.impl;

import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.coocaa.ops.common.tool.base.InstanceUtil;
import com.coocaa.ops.common.tool.date.DateUtil;
import com.coocaa.ops.common.tool.string.StringUtil;
import com.coocaa.ops.plus.dashboard.v1.codegen.constant.ProjectConstants;
import com.coocaa.ops.plus.dashboard.v1.codegen.constant.TypeConstants;
import com.coocaa.ops.plus.dashboard.v1.codegen.convert.ClassNameConvert;
import com.coocaa.ops.plus.dashboard.v1.codegen.convert.PackagePathConvert;
import com.coocaa.ops.plus.dashboard.v1.codegen.model.*;
import com.coocaa.ops.plus.dashboard.v1.codegen.provider.ICodeGenProvider;
import com.coocaa.ops.plus.dashboard.v1.codegen.util.VmTemplateUtil;
import com.coocaa.ops.plus.dashboard.v1.codegen.validator.CodeGenValidator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.VelocityContext;
import org.springframework.util.Assert;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * @author bijiahao
 * @description
 * @date 2020/1/19 10:14
 */
public class BaseCodeGenProviderImpl implements ICodeGenProvider {
    private Map<String, String> templates;

    private Map<String, String> packages;

    private Map<String, String> names;


    private String outputDir;

    private Integer userProjectPath;
    private VelocityContext context;

    @Override
    public void execute(CodeGenConfigModel model) throws IOException {
        // 获取上下文
        Map<String, VelocityContext> ctxData = analyzeData(model);
        // 循环生成文件
        for (Map.Entry<String, VelocityContext> ctx : ctxData.entrySet()) {
            batchOutput(model, ctx.getValue());
        }
    }

    protected Map<String, String> getTemplates(TemplateConfigModel templateConfigModel) {
        return InstanceUtil.obj2Map(templateConfigModel);
    }

    protected Map<String, String> getPackages(TableInfoModel tableInfo, CodeGenConfigModel model) {
        return PackagePathConvert.convertPackagePath(tableInfo.getTableConfigList(), model.getPackageConfig());
    }

    protected Map<String, String> getNames(String tableName2ClassName, CodeGenConfigModel model) {
        return ClassNameConvert.convertName(tableName2ClassName, model.getClassNameConfig());
    }


    /*************** ===私有方法=== ********************* ===start=== ***************************************/

    private void batchOutput(CodeGenConfigModel model, VelocityContext context) throws IOException {
        boolean isOriginTableOnly = CodeGenValidator.isOriginTableOnly(model);

        TableInfoModel tableInfo = (TableInfoModel) context.get("table");
        //初始化
        init(model, context);

        List<TableConfigInfo> tableConfigInfoList = tableInfo.getTableConfigList();
        for (TableConfigInfo tableConfigInfo : tableConfigInfoList) {
            handleEachTableConfig(tableConfigInfo, isOriginTableOnly);
        }
    }

    private void init(CodeGenConfigModel model, VelocityContext context) {
        TemplateConfigModel templateConfigModel = model.getTemplateConfig();
        Map<String, String> templates = this.getTemplates(templateConfigModel);
        this.templates = templates;
        Map<String, String> packages = (Map<String, String>) context.get("package");
        Map<String, String> names = (Map<String, String>) context.get("name");
        this.packages = packages;
        this.names = names;
        this.outputDir = model.getOutputDir();
        this.userProjectPath = model.getUseProjectPath();
        this.context = context;
    }

    private void handleEachTableConfig(TableConfigInfo tableConfigInfo, boolean isOriginTableOnly ) throws IOException {
        String type = tableConfigInfo.getType();
        String packPath = packages.get(type);
        List<String> types = getTypes(type, isOriginTableOnly);
        writeFile(types, tableConfigInfo, packPath);
    }

    protected List<String> getTypes(String type, boolean isOriginTableOnly) {
        List<String> types = new ArrayList<>(2);
        if (TypeConstants.PROVIDER.equals(type)) {
            //有查询和更新的provider接口
            types.add(TypeConstants.QUERY_PROVIDER);
            types.add(TypeConstants.UPDATE_PROVIDER);
        } else if (TypeConstants.PROVIDER_IMPL.equals(type)) {
            //有查询和更新的provider接口实现
            types.add(TypeConstants.QUERY_PROVIDER_IMPL);
            types.add(TypeConstants.UPDATE_PROVIDER_IMPL);

        } else if (TypeConstants.SERVICE.equals(type)) {
            //有查询和更新的service接口
            types.add(TypeConstants.QUERY_SERVICE);
            types.add(TypeConstants.UPDATE_SERVICE);
        } else if (TypeConstants.SERVICE_IMPL.equals(type)) {
            //有查询和更新的service接口实现
            types.add(TypeConstants.QUERY_SERVICE_IMPL);
            types.add(TypeConstants.UPDATE_SERVICE_IMPL);
        } else {
            types.add(type);
        }
        return types;
    }

    /**
     * 写文件
     *
     * @param types
     * @param tableConfig
     * @param packPath
     * @throws IOException
     */
    private void writeFile(List<String> types, TableConfigInfo tableConfig, String packPath) throws IOException {
        for (String type : types) {
            String templateFilePath = getTemplateFilePath(type);
            String outputFilePath = getOutputFilePath(type, tableConfig, packPath);
            writeFile(templateFilePath, tableConfig, outputFilePath);
        }
    }

    /**
     * 写文件
     *
     * @param templateFilePath
     * @param tableConfig
     * @param outputFilePath
     * @throws IOException
     */
    private void writeFile(String templateFilePath, TableConfigInfo tableConfig, String outputFilePath) throws IOException {
        if (isCreate(outputFilePath, tableConfig)) {
            VmTemplateUtil.vmToFile(context, templateFilePath, outputFilePath);
        }
    }

    /**
     * 获取模板路径
     *
     * @param type
     * @return
     */
    private String getTemplateFilePath(String type) {
        Object data = templates.get(type);
        Assert.notNull(data, "没有对应的模板文件,type:" + type);
        return data.toString();
    }

    /**
     * 生成代码的存放路径
     *
     * @param type
     * @param tableConfig
     * @param packPath
     * @return
     */
    private String getOutputFilePath(String type, TableConfigInfo tableConfig, String packPath) {

        String fileMame = names.get(type);
        StringBuilder pathBuilder = new StringBuilder();
        pathBuilder.append(outputDir).append("/")
                .append(tableConfig.getModuleName());
        if (null != userProjectPath && userProjectPath == 1) {
            pathBuilder.append("/")
                    .append(ProjectConstants.SRC_MAIN_JAVA)
                    .append("/")
                    .append(packPath.replace(".", "/"));

        }
        String typePathDir = getTypePathDir(tableConfig.getType(), userProjectPath);
        pathBuilder.append("/")
                .append(typePathDir)
                .append("/")
                .append(fileMame);
        String suffix = ConstVal.JAVA_SUFFIX;
        if (TypeConstants.XML.equals(tableConfig.getType())) {
            suffix = ConstVal.XML_SUFFIX;
        }
        pathBuilder.append(suffix);
        return pathBuilder.toString();
    }

    /**
     * 子包名路径目录设置
     *
     * @param type
     * @return
     */
    protected String getTypePathDir(String type, Integer userProjectPath) {
        if (TypeConstants.CONTROLLER.equalsIgnoreCase(type)) {
            return "";
        }
        if (Objects.equals(1, userProjectPath)) {
            return "";
        } else {
            if (TypeConstants.XML.equalsIgnoreCase(type)) {
                return TypeConstants.MAPPER;
            }
            return StringUtil.convertCamel2Path(type);
        }
    }

    /**
     * 检测文件是否存在
     *
     * @return 是否
     */
    protected boolean isCreate(String filePath, TableConfigInfo tableConfigInfo) {
        File file = new File(filePath);
        Integer disable = tableConfigInfo.getDisable();
        Integer fileOverride = tableConfigInfo.getFileOverride();
        boolean isCanUse = null == disable || disable == 0;
        boolean isCanWrite = !file.exists() || (fileOverride != null && fileOverride == 1);
        return isCanUse && isCanWrite;
    }

    /**
     * 设置模板需要的数据
     *
     * @param model
     * @return
     */
    private Map<String, VelocityContext> analyzeData(CodeGenConfigModel model) {
        List<TableInfoModel> tableInfoList = model.getTableList();
        VelocityContext ctx;
        Map<String, VelocityContext> ctxData = new HashMap<>();
        for (TableInfoModel tableInfo : tableInfoList) {
            ctx = new VelocityContext();
            String tableName2ClassName = ClassNameConvert.convertTableName2ClassName(tableInfo.getTableName());
            String entityName = ClassNameConvert.convert2EntityName(tableName2ClassName, model.getClassNameConfig());
            ctx.put("entityName", entityName);
            ctx.put("tableName2ClassName", tableName2ClassName);
            String date = DateUtil.getCurDateStr("yyyy-MM-dd HH:mm:ss");
            ctx.put("author", model.getAuthor());
            ctx.put("date", date);
            ctx.put("idType", tableInfo.getIdType());
            handleAutoId(tableInfo, ctx);
            //设置菜单elid和请求url
            handleSetElidAndReuqestUrl(tableInfo);
            ctx.put("table", tableInfo);
            //super类
            StrategyConfigModel strategyConfig = model.getStrategyConfig();
            ctx.put("superEntityClass", strategyConfig.getSuperEntityClass());
            ctx.put("superModelClass", strategyConfig.getSuperModelClass());
            ctx.put("superMapperClass", strategyConfig.getSuperMapperClass());
            ctx.put("superBizImplClass", strategyConfig.getSuperBizImplClass());
            ctx.put("superControllerClass", strategyConfig.getSuperControllerClass());
            //supper类名
            String superEntityName = getSuperClassName(strategyConfig.getSuperEntityClass());
            String superMapperName = getSuperClassName(strategyConfig.getSuperMapperClass());
            String superModelName = getSuperClassName(strategyConfig.getSuperModelClass());
            String superBizImplName = getSuperClassName(strategyConfig.getSuperBizImplClass());
            String superControllerName = getSuperClassName(strategyConfig.getSuperControllerClass());
            ctx.put("superEntityName", superEntityName);
            ctx.put("superModelName", superModelName);
            ctx.put("superBizImplName", superBizImplName);
            ctx.put("superMapperName", superMapperName);
            ctx.put("superControllerName", superControllerName);
            Map<String, String> packagePath = getPackages(tableInfo, model);
            ctx.put("package", packagePath);
            Map<String, String> name = this.getNames(tableName2ClassName, model);
            ctx.put("name", name);
            ctxData.put(tableName2ClassName, ctx);
        }
        return ctxData;
    }

    /**
     * 设置菜单elid和请求url
     *
     * @param tableInfo
     */
    private void handleSetElidAndReuqestUrl(TableInfoModel tableInfo) {
        String tableName = tableInfo.getTableName();
        //如果有“_”下划线,取第一个下划线之前的
        Integer index1 = tableName.indexOf("_");
        Integer index = -1;
        if (index1 > -1){
            Integer index2 = tableName.indexOf("_", index1 + 1);
            index = index2 > -1 ? index2 : index1;
        }
        String tableNameTrim = index > -1 ? tableName.substring(0, index) : tableName;
        tableNameTrim = StringUtil.underlineToCamel(tableNameTrim);
        if (StringUtils.isBlank(tableInfo.getElid())) {
            tableInfo.setElid(tableNameTrim);
        }
        if (StringUtils.isBlank(tableInfo.getRequestUrl())) {
            tableInfo.setRequestUrl(tableNameTrim);
        }
    }

    /**
     * 设置自增ID
     *
     * @param tableInfo
     * @param ctx
     */
    private void handleAutoId(TableInfoModel tableInfo, VelocityContext ctx) {
        List<ColumnConfigModel> columnConfigModelList = tableInfo.getColumnList();
        for (ColumnConfigModel columnConfigModel : columnConfigModelList) {
            if (isPriKey(columnConfigModel)) {
                columnConfigModel.setKeyFlag(true);
                boolean isExcludeAutoId = isExcludeAutoId(columnConfigModel);
                ctx.put("isExcludeAutoId", isExcludeAutoId);
                ctx.put("priKeyColumnName", columnConfigModel.getColumnName());
                ctx.put("priKeyFieldName", columnConfigModel.getFieldName());
                handleImportPakageForEntity(tableInfo);
                break;
            }
        }
    }

    /**
     * 设置实体类需要导入的包
     *
     * @param tableInfo
     */
    private static void handleImportPakageForEntity(TableInfoModel tableInfo) {
        List<ColumnConfigModel> columnConfigModelList = tableInfo.getColumnList();
        for (ColumnConfigModel columnConfigModel : columnConfigModelList) {
            if ("Date".equals(columnConfigModel.getJavaType())) {
                tableInfo.getImportPackages().add(Date.class.getCanonicalName());
                return;
            }
        }
        List<ColumnConfigModel> notExistColumnList = tableInfo.getNotExistColumnList();
        if (CollectionUtils.isNotEmpty(notExistColumnList)) {
            for (ColumnConfigModel columnConfig : notExistColumnList) {
                if ("Date".equals(columnConfig.getJavaType())) {
                    tableInfo.getImportPackages().add(Date.class.getCanonicalName());
                    return;
                }
            }
        }
    }

    /**
     * 是否需要排除主键
     *
     * @param columnConfigModel
     * @return
     */
    private static boolean isExcludeAutoId(ColumnConfigModel columnConfigModel) {
        return !"id".equals(columnConfigModel.getColumnName());
    }

    /**
     * 是否是主键
     *
     * @param columnConfig
     * @return
     */
    private static boolean isPriKey(ColumnConfigModel columnConfig) {
        return "PRI".equals(columnConfig.getKey());
    }

    /**
     * <p>
     * 获取类名
     * </p>
     *
     * @param classPath
     * @return
     */
    private static String getSuperClassName(String classPath) {
        if (StringUtils.isEmpty(classPath)) {
            return null;
        }
        return classPath.substring(classPath.lastIndexOf(".") + 1);
    }
}
